﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace ThreadSafeControls {
    internal sealed class SynchronizedObjectCollection<T, TList> : IList<T> where TList : IList<T>, ICollection {

        private readonly TList _list;

        public SynchronizedObjectCollection(TList list) {
            if (list == null)
                throw new ArgumentNullException("list");

            _list = list;
        }

        public T this[int index] {
            get {
                return ReturnAsync(() => ((IList<T>)_list)[index]);
            }
            set {
                UpdateAsync(() => ((IList<T>)_list)[index] = value);
            }
        }

        public int Count {
            get { return ReturnAsync(() => ((ICollection<T>)_list).Count); }
        }

        public bool IsReadOnly {
            get { return ((ICollection<T>)_list).IsReadOnly; }
        }

        public bool IsSynchronized {
            get { return true; }
        }

        public object SyncRoot {
            get { return _list.SyncRoot; }
        }

        public void Add(T item) {
            UpdateAsync(() => _list.Add(item));
        }

        public void Clear() {
            UpdateAsync(() => ((ICollection<T>)_list).Clear());
        }

        public bool Contains(T item) {
            return ReturnAsync(() => _list.Contains(item));
        }

        public int IndexOf(T item) {
            return ReturnAsync(() => _list.IndexOf(item));
        }

        public void Insert(int index, T item) {
            UpdateAsync(() => _list.Insert(index, item));
        }

        public bool Remove(T item) {
            return ReturnAsync(() => _list.Remove(item));
        }

        public void RemoveAt(int index) {
            UpdateAsync(() => ((IList<T>)_list).RemoveAt(index));
        }

        public void CopyTo(T[] array, int arrayIndex) {
            UpdateAsync(() => _list.CopyTo(array, arrayIndex));
        }

        public IEnumerator<T> GetEnumerator() {
            T[] array = null;

            UpdateAsync(
                () => {
                    array = new T[((ICollection<T>)_list).Count];
                    _list.CopyTo(array, 0);
                }
            );

            return ((IEnumerable<T>)array).GetEnumerator();
        }

        private void UpdateAsync(Action action) {
            lock (_list.SyncRoot)
                action();
        }

        private TResult ReturnAsync<TResult>(Func<TResult> func) {
            lock (_list.SyncRoot)
                return func();
        }

        #region Explicit Interface Members

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion

    }
}
